#include "OpenGLRenderAPI.hpp"

#include "../../Core/Application.hpp"
#include "../../Primitive/GeometryGenerator.h"
#include "../../Render/RenderCommand.hpp"
#include "Buffers/OpenGLIndexBuffer.h"
#include "Buffers/OpenGLVertexArray.h"
#include "Buffers/OpenGLTextureBuffer.h"
#include "Buffers/OpenGLFrameBuffer.h"

namespace Kriko
{
	void OpenGLRenderAPI::Init()
	{
		//TODO DEBUG
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		glEnable(GL_DEPTH_TEST);
	}
	void OpenGLRenderAPI::SetViewport(uint32_t p_x, uint32_t p_y, uint32_t p_width, uint32_t p_height)
	{
		glViewport(p_x, p_y, p_width, p_height);
	}
	void OpenGLRenderAPI::SetClearColor(const glm::vec4& p_color)
	{
		glClearColor(p_color.r, p_color.g, p_color.b, p_color.a);
	}
	void OpenGLRenderAPI::Clear()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}
	void OpenGLRenderAPI::SetCamera(Camera* p_camera)
	{
		this->pCamera = p_camera;
	}
	void OpenGLRenderAPI::SetRenderObject(RenderObject* renderObject)
	{
		auto meshPtr = renderObject->GetMeshPtr();
		OpenGLBuffer* vBuffer = new OpenGLBuffer(meshPtr->GetVertices());
		OpenGLBuffer* uvBuffer = new OpenGLBuffer(meshPtr->GetUVs());
		OpenGLBuffer* nBuffer = new OpenGLBuffer(meshPtr->GetNormals());

		OpenGLVertexArray* vArray = new OpenGLVertexArray();
		vArray->AddBuffer(vBuffer, 0);
		vArray->AddBuffer(uvBuffer, 1);
		vArray->AddBuffer(nBuffer, 2);

		OpenGLIndexBuffer* iBuffer = new OpenGLIndexBuffer(meshPtr->GetIndices());

		auto textureBuffer = new OpenGLTextureBuffer(renderObject->GetTextureRef());

		AddBufferToVector(renderObject, iBuffer, vArray, textureBuffer);
	}
	int OpenGLRenderAPI::SetFrameBufferSize(size_t width, size_t height)
	{
		if (pFrameBuffer != nullptr)
		{
			delete pFrameBuffer;
			pFrameBuffer = nullptr;
		}
		pFrameBuffer = new OpenGLFrameBuffer(width, width);
		return pFrameBuffer->GetFrameTexture();
	}


	void OpenGLRenderAPI::AddBufferToVector(RenderObject* renderObject, OpenGLIndexBuffer* iBuffer, OpenGLVertexArray* vBuffer, OpenGLTextureBuffer* texture)
	{
		this->RenderObjects.push_back(renderObject);
		this->VBuffers.push_back(vBuffer);
		this->IBuffers.push_back(iBuffer);
		this->TextureBuffers.push_back(texture);
	}

	void OpenGLRenderAPI::Render()
	{
		std::vector<RenderObject*>::size_type size = RenderObjects.size();
		for (int i = 0; i < size; ++ i)
		{
			auto shader = RenderObjects[i]->GetShaderPtr();
			auto vArray = VBuffers[i];
			auto iBuffer = IBuffers[i];
			auto texture = TextureBuffers[i];

			shader->Bind();
			texture->Bind();
			vArray->Bind();
			iBuffer->Bind();

			auto transform = RenderObjects[i]->GetTransformPtr();
			// ctor MVP
			glm::mat4 model = glm::mat4(1.0f);
			model = transform->GetMatrix();
			glm::mat4 view = glm::mat4(1.0f);
			view = pCamera->GetViewMatrix();
			glm::mat4 projection = glm::mat4(1.0f);
			projection = pCamera->GetProjectionMatrix();
			
			// set shader param
			shader->setMat4("projection", projection);
			shader->setMat4("view", view);
			shader->setMat4("model", model);

			glDrawElements(GL_TRIANGLES, iBuffer->GetCount(), GL_UNSIGNED_INT, 0);

			iBuffer->Unbind();
			vArray->Unbind();
			texture->Unbind();
		}
	}

	int OpenGLRenderAPI::FrameBufferRender()
	{
		if (pFrameBuffer == nullptr)
		{
			g_Logger.Log(BaseLib::LogTag::Error, "Need to call Create FrameBuffer First");
			return -1;
		}
		pFrameBuffer->Bind();
		glEnable(GL_DEPTH_TEST);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		this->Render();
		pFrameBuffer->Unbind();

		return pFrameBuffer->GetFrameTexture();
	}


}